#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"

class SocketServer
{
public:
    void Socket()
    {
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(socket_fd == -1)
        {
            lg(Fatal, "socket err: %s[%d]", strerror(errno), errno);
            exit(1);
        }
    }

    void Bind(uint16_t& port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        int b = bind(socket_fd, (const sockaddr*)&local, sizeof(local));
        if(b == -1)
        {
            lg(Fatal, "bind err: %s[%d]", strerror(errno), errno);
            exit(2);
        }
    }

    void Listen()
    {
        int l = listen(socket_fd, 10);
        if(l == -1)
        {
            lg(Fatal, "listen err: %s[%d]", strerror(errno), errno);
            exit(3);
        }
    }

    int Accept(struct sockaddr_in* accept_sockaddr)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int afd = accept(socket_fd, (sockaddr*)&peer, &len);
        if(afd == -1)
        {
            lg(Fatal, "accept err: %s[%d]", strerror(errno), errno);
            return -1;
        }

        *accept_sockaddr = peer;
        return afd;
    }

    bool Connect(const uint16_t& port, const std::string& ip)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
        int c = connect(socket_fd, (const sockaddr*)&peer, sizeof(peer));
        if(c == -1)
        {
            lg(Fatal, "connect err: %s[%d]", strerror(errno), errno);
            return false;
        }

        return true;
    }

    void Close()
    {
        close(socket_fd);
    }

    int Fd()
    {
        return socket_fd;
    }

private:
    int socket_fd;
};